Skip to content

Conversation

@dlford
Copy link
Contributor

@dlford dlford commented Aug 31, 2025

fixes #7431

Summary

The code widget executes the onChange callback anytime lang is changed, including when initializing the value from props. This causes the CMS to act as if there are unsaved changes any time a document is loaded with a saved language in the code widget.

Test plan

Using the below config and index, create a new blog post and add a code component, set the language and save. Reload the page, you should see the "changes saved" message in the top left of the CMS instead of "unsaved changes".

config.yml

local_backend: true
backend:
  name: test-repo
media_folder: static/media
public_folder: /media

collections:
  - name: blog
    label: Blog Posts
    label_singular: Blog Post
    folder: content/blog
    create: true
    preview_path: "{{fields.slug}}"
    identifier_field: slug
    fields:
    - label: Title
      name: title
      hint: The title should ideally be between 5 and 60 characters long for SEO
      widget: string
      required: true
    - label: Post Content
      name: body
      widget: markdown
      required: false
      editor_components:
      - code

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Decap CMS</title>
  </head>
  <body>
    <script src="dist/decap-cms.js"></script>
    <script>
      const CMS = window.CMS;

      const langMap = {
        js: 'javascript',
        ts: 'typescript',
        sh: 'shell',
        'go-html-template': 'go',
        yml: 'yaml',
        cfg: 'ini',
        txt: '',
      };

      CMS.registerEditorComponent({
        id: 'code',
        label: 'Code',
        summary: '{{fields.title}}',
        collapsed: true,
        fields: [
          {
            name: 'title',
            label: 'Title',
            widget: 'string',
            default: '',
            required: false,
          },
          {
            name: 'hl',
            label: 'Highlight Lines',
            hint: 'Use spaces to separate multiple lines and hyphens for ranges, e.g. `1 3-5`',
            widget: 'string',
            default: '',
            required: false,
          },
          {
            name: 'code',
            label: 'Code',
            widget: 'code',
            required: true,
          },
        ],
        pattern:
          /{{<code[ \n]?(lang="([^"]+)?")?[ \n]?(title="([^"]+)?")?[ \n]?(hl="([^"]+)?")?>}}\n+```([a-zA-Z]+)?\n((?!```).*?$)\n```\n+{{<\/code>}}/ms,
        fromBlock: function (match) {
          return {
            title: match[4],
            hl: match[6],
            code: {
              lang: match[2] ? langMap[match[2]] || match[2] : '',
              code: match[8] || '',
            },
          };
        },
        toBlock: function ({ title, hl, code }) {
          let lang = code?.lang ? langMap[code.lang] || code.lang : '';

          let args = '';
          if (lang) args += ` lang="${lang}"`;
          if (title) args += ` title="${title}"`;
          if (hl) args += ` hl="${hl}"`;
          return `{{<code${args}>}}\n\`\`\`\n${code?.code}\n\`\`\`\n{{</code>}}`;
        },
        toPreview: function ({ title, hl, code }) {
          return `<code>Language: ${code?.lang}</code>\n<code>Title: ${
            title || ''
          }</code>\n<code>Highlight Lines: ${hl || ''}</code>\n<pre><code>\n${
            code?.code || ''
          }\n</code></pre>`;
        },
      });
    </script>
  </body>
</html>

Checklist

Please add a x inside each checkbox:

@dlford dlford requested a review from a team as a code owner August 31, 2025 16:57
martinjagodic
martinjagodic previously approved these changes Oct 7, 2025
@martinjagodic martinjagodic requested a review from Copilot October 7, 2025 09:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes an issue where the code widget incorrectly triggers the onChange callback when initializing the language value from props, causing the CMS to show "unsaved changes" status when loading a document with a saved language setting.

  • Added state tracking to identify initial language initialization
  • Modified the language change handler to ignore the initial language setting
  • Prevented false positive unsaved changes state on document load

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +134 to +136
this.setState({ isLangInitialized: true });

this.handleChangeCodeMirrorProps(changedProps, shouldIgnoreLangChange);
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting isLangInitialized to true should be done after the language change is processed, not before. If handleChangeCodeMirrorProps throws an error, the flag will be incorrectly set to true even though initialization didn't complete successfully.

Suggested change
this.setState({ isLangInitialized: true });
this.handleChangeCodeMirrorProps(changedProps, shouldIgnoreLangChange);
this.handleChangeCodeMirrorProps(changedProps, shouldIgnoreLangChange);
this.setState({ isLangInitialized: true });

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with this, the ordering here is intentional for graceful failure; if for some reason handleChangeCodeMirrorProps errors we would rather falsely report changes when there are none than potentially ignore actual changes that have been made.

@martinjagodic
Copy link
Member

@dlford please check if Copilot makes sense here.

@martinjagodic martinjagodic enabled auto-merge (squash) October 8, 2025 07:16
@martinjagodic martinjagodic merged commit 7576d1c into decaporg:main Oct 8, 2025
7 checks passed
ascender1729 pushed a commit to ascender1729/decap-cms that referenced this pull request Oct 8, 2025
…nsaved changes state (decaporg#7588)

* fix: 7431 - code widget Ignore initial lang change to prevent false unsaved changes state

* 7588: apply copilot nitpick

Co-authored-by: Copilot <[email protected]>

---------

Co-authored-by: Martin Jagodic <[email protected]>
Co-authored-by: Copilot <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Code block with language annotation causes report of unsaved changes

2 participants